package netty.test3Selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * 1，启动服务端后 DOS窗口中通过  telnet 127.0.0.1 6666 连接到这个服务器
 * 2，通过  ctrl+]   就可以发送数据了  比如发送hello 命令为 send hello
 */
public class NioServer {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel server=ServerSocketChannel.open();
        Selector selector=Selector.open();

        //1,绑定端口
        server.socket().bind(new InetSocketAddress(6666));
        //2,设置为非阻塞 ,与selector一起使用的时候 必须切换到非阻塞模式这意味着不能将FileChannel与Selector一起使用，
        //因为FileChannel不能切换到非阻塞模式。而套接字通道都可以
        server.configureBlocking(false);

        //3,将channel 注册到selector上（关心事件设置为 连接事件 OP_ACCEPT）
        server.register(selector, SelectionKey.OP_ACCEPT);

        //4,循环等待客户端连接
        while(true){
            int count=selector.select(2000);
            if(count==0){//表示没有事件发生
                System.out.println("服务器等待了 2 s 无连接");
                continue;
            }
            //5,返回关注事件的集合，如果>0 就表示获取到了
            // 通过  SelectionKey 也能获取到channel
            Set<SelectionKey> keys=selector.selectedKeys();// selector.keys()返回所有注册到selector上的key,注意这两个方法的区别
            //6,迭代
            Iterator<SelectionKey> it=keys.iterator();
            while(it.hasNext()){
                SelectionKey key=it.next();
                // 7,看看返回的是那种类型的事件，然后做相应的处理
                if(key.isAcceptable()){ //（1）注意，第一次肯定进到这里来，在 9 里面把socket 注册到selector后，有数据来的话，会进入到其他分支里，比如 进入到key.isReadable() 分支里
                    // 8,获取这个连接的 socketChannel，accept()本来是阻塞的，但是这里不会，因为
                    //上面判断了key.isAcceptable()，已经是就绪事件了
                    SocketChannel socketChannel=server.accept();
                    // 9,这里要将获取到的socketChannel 设置为非阻塞，否则会出错
                    socketChannel.configureBlocking(false);
                    System.out.println("客户端连接成功 生成socketChannel hashcode="+socketChannel.hashCode());
                    //10，将当前的这个channel 也注册到 selector 上；(当前通道有读事件的时候)
                    //同时也可以给当前channel 关联一个buffer
                    socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }

                if(key.isReadable()){//op_read
                    //11 ,通过key 反向获取到channel (需要强制转换)
                    SocketChannel channel = (SocketChannel)key.channel();
                    //12，获取到该channel 关联的buffer
                    ByteBuffer buffer = (ByteBuffer)key.attachment();
                    //13,把channel 中的数据写入到 buffer
                    channel.read(buffer);
                    System.out.println("从客户段获取到的数据为---》"+new String(buffer.array()));
                }

                if(key.isConnectable()){

                }

                if(key.isWritable()){

                }

                // 重要：要手动移除当前selectionKey,防止重复操作
                it.remove();
            }

        }
    }
}
